#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <dirent.h>
#include <unistd.h>
#include <vector>
#include <sstream>
#include <fstream>
#include <sys/io.h>
#include <sys/times.h>
#include <iomanip> // setw()
using namespace std;

#include "Core"
using namespace Eigen;

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/eigen.hpp>
using namespace cv;
#include "opencv2/ximgproc.hpp"
using namespace cv::ximgproc;

#include "segFlag.hpp"
#include "alexNetForMnist.hpp"

// ----------------------------------------------------- //
static bool readVideo(string strVideoPath);
static void onMouse( int event, int x, int y, int, void* );
static void LeftButtonClick(int x, int y);
static void LeftButtonDoubleClick(int x, int y);
void for_showing();

// global variables ------------------------------------ //
cv::Mat CurRGB, CurMono, ShowImg;
int g_curKeyBoardPushed;
int g_FrmNum = 0, g_curFrm = 0;
std::vector<segfg::sARct > g_curRegs;
std::vector<segfg::sAPosi> regs;
// ----------------------------------------------------- //
int main(int argc, char** argv)
{
	cout << ">> ----" << "\n" << endl;
	
	cv::namedWindow("win1", WINDOW_NORMAL);
	resizeWindow("win1", 1920, 1080);
	setMouseCallback("win1", onMouse, 0);
	if ((access(argv[1], 0)) != -1) {
		readVideo(argv[1]);
	}
	else {
		cout << ">> error. argv[1], file not exists" << endl;
	} 
	
	cout << "\n" << ">> ----" << endl;
	return 0;
}

static bool readVideo(string strVideoPath)
{
	// Create an AlexCNN model and load parameters that have been traing well in Tensorflow
	PiaAlexNet cNN;
	CreateAlexForMnist(cNN, "./ocr_python/storage_conv/mnistConvModel.npz");
	cNN.Print();
	// openning video
	cv::VideoCapture capture;
	capture = cv::VideoCapture(strVideoPath.c_str());
	double dFrameNum = -1, dFPS = -1, dImgWid = -1, dImgHei = -1;
	if (capture.isOpened())
	{
		dFrameNum = capture.get(CV_CAP_PROP_FRAME_COUNT);
		dFPS      = capture.get(CV_CAP_PROP_FPS);
		dImgWid   = capture.get(CV_CAP_PROP_FRAME_WIDTH);
		dImgHei   = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
		g_FrmNum  = (int)dFrameNum;
		int sta = 355;
		int end = dFrameNum - 1;
		for (;;) {
			if (g_curFrm < sta) {
				g_curFrm = sta;
			}
			if (g_curFrm > end) {
				g_curFrm = end;
			}
			capture.set(CV_CAP_PROP_POS_FRAMES, double(g_curFrm));
			capture.read(CurRGB);
//			resize(CurRGB, CurRGB, Size(), 0.5, 0.5, INTER_LINEAR);
			// process
			cvtColor(CurRGB, CurMono, COLOR_BGR2GRAY);
			regs.clear();
			segfg::segmentFlag(CurMono, regs);
			for (int r = 0; r < (int)regs.size(); r++) {
				for (int i = 0; i < (int)regs[r].ocImgs.size(); i++) {
					vector<MatrixXd> eigenImgs(1);
					cv2eigen(regs[r].ocImgs[i], eigenImgs[0]);
					eigenImgs[0] /= 255.f;
					VectorXd Output;
					cNN.RunImgs(eigenImgs, Output);
					double maxvla = 0.f; int maxidx;
					for (int j = 0; j < Output.rows(); j++) {
						if (Output(j, 0) > maxvla) {
							maxvla = Output(j, 0);
							maxidx = j;
						}
					}
					if (maxvla > 0.6f) 
						regs[r].ocNums[i] = maxidx;
					else
						regs[r].ocNums[i] = -1;
				}
			}
			for_showing();
			g_curKeyBoardPushed = waitKey();
			switch (g_curKeyBoardPushed) 
			{
			case 113: { // q
				g_curFrm -= 1;
				if (g_curFrm < sta) {
					g_curFrm = sta;
				}
				break;}
			case 101: { // e
				g_curFrm += 1;
				if (g_curFrm > end) {
					g_curFrm = end;
				}
				break;}
			case 115: { // s
				std::size_t found0 = strVideoPath.rfind(".");
				std::size_t found1 = strVideoPath.rfind("/");
				if (found0 != std::string::npos && found1 != std::string::npos) {
					stringstream ss;
					ss << g_curFrm;
					string str = "./data" + 
					             strVideoPath.substr(found1, found0 - found1) + 
					             "_" + ss.str() + ".jpg";
					if (access(str.c_str(), 0) == -1) {
						imwrite(str.c_str(), CurRGB);
						cout << ">> save success, info:" << str.c_str() << endl;
					}
					else
						cout << ">> file exists" << endl;
				}
				break;
			}
			case 27: { 	// esc
				g_curFrm = -1;
				break;
				}
			default:	// do nothing
				break;
			}
			if (g_curFrm < 0) {
				break;
			}
		}
		capture.release();
		CurRGB.release();
		CurMono.release();
		destroyWindow("win1");
		cout << ">> release all memory" << endl;
	}
	else { 
		cout << ">> error. fail to open video. file: " << strVideoPath.c_str() << endl; 
		return false; 
	}
	
	return true;
}

// paint region
void for_showing() 
{
	CurRGB.copyTo(ShowImg);
	stringstream ss;
	ss << g_curFrm;
	ss << " / "; 
	ss << g_FrmNum;
	ss << ", region: ";
	ss << (int)regs.size();
	string str = ss.str();
	cv::putText(ShowImg, str.c_str(), cv::Point(5, 30), 
				FONT_HERSHEY_SIMPLEX, 0.8, 
				Scalar(0, 255, 255, 255), 2, 8);
	for (int i = 0; i < (int)regs.size(); i++) {
		std::vector<std::vector<Point> > bd(1);
		bd[0] = regs[i].bounder;
//		drawContours(ShowImg, bd, 0, Scalar(255,0,255), 2, LINE_8);
		circle(ShowImg, regs[i].poly[3], 4, Scalar(255,0,255));
		for (int j = 0; j < (int)regs[i].ocBounders.size(); j++) {
			Scalar color11 = Scalar((unsigned)theRNG() & 255, 
	                                    (unsigned)theRNG() & 255, 
	                                    (unsigned)theRNG() & 255);
			drawContours(ShowImg, regs[i].ocBounders, j, color11, 2, LINE_8);
		}
		cv::Rect rct = boundingRect(regs[i].bounder);
		stringstream ss;
		for (int j = 0; j < (int)regs[i].ocImgs.size(); j++) {
			if (regs[i].ocNums[j] >= 0) {
				ss << regs[i].ocNums[j];
			}
			else
				ss << "*";
			cout << i << ", " << j << ", " << regs[i].ocNums[j] << endl;
		}
		cv::putText(ShowImg, ss.str(), cv::Point(rct.x, rct.y), FONT_HERSHEY_SIMPLEX, 0.8, 
					Scalar(255, 0, 0), 2, 8);
	}
	imshow("win1", ShowImg);
}



static void onMouse( int event, int x, int y, int, void* )
{
	switch (event) 
	{
	case CV_EVENT_LBUTTONDOWN:
	LeftButtonClick(x, y);
	break;
	case CV_EVENT_LBUTTONUP:
	break;
	case CV_EVENT_RBUTTONDOWN:
	break;
	case CV_EVENT_LBUTTONDBLCLK:
	break;
	}
}

static void LeftButtonClick(int x, int y)
{
	// erase a region
	std::vector<segfg::sARct>::iterator itor;
	for (itor = g_curRegs.begin(); itor != g_curRegs.end(); ) {
		int nIsIn = cv::pointPolygonTest(itor->bounder, Point2f(x, y), false);
		if (nIsIn >= 0) {
			itor = g_curRegs.erase(itor);
		}
		else
			itor++;
	}
	for_showing();
}

static void LeftButtonDoubleClick(int x, int y)
{
	
}









